home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / PROGRAMM / DB_CLIPP / 0769B.ZIP / DBF2TPV3.PAS < prev    next >
Pascal/Delphi Source File  |  1986-06-29  |  29KB  |  876 lines

  1.    
  2.     
  3.  
  4.   (*
  5.   DBF.PAS version 1.3
  6.   Copyright (C) 1986 By James Troutman
  7.   CompuServe PPN 74746,1567
  8.   Permission is granted to use these routines for non-commercial purposes.
  9.   For commercial use, please request permission via EasyPlex.
  10.  
  11.   Version 1.3 -- Routines to access ANY dBASE .DBF file (2, 3, or 3+).  In
  12.   addition to support for dBASE 2 files, a CreateDbf procedure has been
  13.   added. Sample program simulates DISPLAY STRUCTURE and LIST and copies any
  14.   DBF file to any other (e.g., converts a dBASE 2 file to dBASE 3). Requires
  15.   Turbo 3.01A and PC DOS.
  16.  
  17.  
  18.    Revision history
  19.    1.1  - 5/6/86 - update header when modifying the .DBF file; write the
  20.    End Of File marker; simplify use.
  21.  
  22.    1.2  - 5/27/86 - removed (some of) the absurdities from the code;
  23.    allocate the current record buffer on the heap rather than in the data
  24.    segment; symbol names changed to avoid conflicts; some error checking
  25.    added.
  26.  
  27.    1.3  - 6/5/86 - added support for dBASE II files; new procedure CreateDbf.
  28.  
  29.                     !!!!ATTENTION!!!!
  30.   If you have downloaded an earlier version of this file, please note that
  31.   several of the TYPEs and VARs have been changed.  You may have to make
  32.   some adjustments to any existing programs you have that use these routines.
  33.  
  34.   The routines in this file present some tools for accessing dBASE II, III, and
  35.   III Plus files from within a Turbo Pascal program.  There is MUCH
  36.   room for improvement: the error checking is simplistic, there is no support
  37.   for memo files, no buffering of data, no support for index files,
  38.   etc. The main routines are:
  39.  
  40.          PROCEDURE OpenDbf(VAR D : dbfRecord;) : Integer;
  41.          PROCEDURE CloseDbf(VAR D : dbfRecord) : Integer;
  42.          PROCEDURE GetDbfRecord(VAR D : dbfRecord; RecNum : Real);
  43.          PROCEDURE PutDbfRecord(VAR D : dbfRecord; RecNum : Real);
  44.          PROCEDURE AppendDbf(VAR D : dbfRecord);
  45.          PROCEDURE CreateDbf(VAR D : dbfRecord; fn : _Str64; n : Integer;
  46.                              flds : _dFields);
  47.  
  48.   After calling one of the procedures, check the status of the Boolean variable
  49.   dbfOK to determine the success or failure of the operation.  If it failed,
  50.   dbfError will contain a value corresponding to the IOResult value or
  51.   to a specially assigned value for several special conditions.  Notice in
  52.   particular that an unsuccessful call to CloseDbf will leave the file status
  53.   unchanged and the memory still allocated.  It is your program's
  54.   responsibility to take appropriate action.
  55.  
  56.   A skeletal program might go something like:
  57.     {$I Dbf.PAS}
  58.     VAR
  59.       D : dbfRecord; { declare your dBASE file variable }
  60.     BEGIN
  61.     D.FileName := 'MyFile.DBF'; { get filename of .dbf file into FileName field
  62.                                   of D variable ...  }
  63.     OpenDbf(D);        { to open the file }
  64.     IF NOT dbfOK THEN { check dbfError and process error };
  65.     {... the rest of your program including calls to
  66.      GetDbfRecord, PutDbfRecord, AppendDbf as needed,
  67.      always remembering to interrogate the two global status
  68.      variables after each procedure call   }
  69.     CloseDbf(D);      { to close the file  }
  70.     IF NOT dbfOK THEN { check dbfError and process error };
  71.     END.
  72.  
  73.   Upon exit from the GetDbfRecord Procedure, the CurRecord of the
  74.   dbfRecord variable points to the current record contents.  Each field
  75.   can be accessed using its offset into the CurRecord^ with the variable
  76.   Off in the Fields^ array.
  77.   Upon entry to the PutDbfRecord Procedure, the CurRecord^ should contain
  78.   the data that you want to write.
  79.   AppendDbf automatically adds a record to the end of the file (the
  80.   CurRecord^ should contain the data that you want to write).
  81.  
  82.   Notice that the OpenDbf routine does allocate a buffer on the heap for
  83.   the current record.  You can, of course, override this by pointing
  84.   CurRecord to any data structure that you wish; HOWEVER, since CloseDbf
  85.   deallocates the buffer, you must repoint CurRecord to its original buffer
  86.   before calling CloseDbf.
  87.  
  88.   See the demo program for some examples.
  89.   If you have any problems with these routines, please
  90.   let me know.  Suggestions for improvements gratefully accepted.
  91.   *)
  92.  
  93. (*
  94. dBASE III Database File Structure
  95. The structure of a dBASE III database file is composed of a
  96. header and data records.  The layout is given below.
  97. dBASE III DATABASE FILE HEADER:
  98. +---------+-------------------+---------------------------------+
  99. |  BYTE   |     CONTENTS      |          MEANING                |
  100. +---------+-------------------+---------------------------------+
  101. |  0      |  1 byte           | dBASE III version number        |
  102. |         |                   |  (03H without a .DBT file)      |
  103. |         |                   |  (83H with a .DBT file)         |
  104. +---------+-------------------+---------------------------------+
  105. |  1-3    |  3 bytes          | date of last update             |
  106. |         |                   |  (YY MM DD) in binary format    |
  107. +---------+-------------------+---------------------------------+
  108. |  4-7    |  32 bit number    | number of records in data file  |
  109. +---------+-------------------+---------------------------------+
  110. |  8-9    |  16 bit number    | length of header structure      |
  111. +---------+-------------------+---------------------------------+
  112. |  10-11  |  16 bit number    | length of the record            |
  113. +---------+-------------------+---------------------------------+
  114. |  12-31  |  20 bytes         | reserved bytes (version 1.00)   |
  115. +---------+-------------------+---------------------------------+
  116. |  32-n   |  32 bytes each    | field descriptor array          |
  117. |         |                   |  (see below)                    | --+
  118. +---------+-------------------+---------------------------------+   |
  119. |  n+1    |  1 byte           | 0DH as the field terminator     |   |
  120. +---------+-------------------+---------------------------------+   |
  121. |
  122. |
  123. A FIELD DESCRIPTOR:      <------------------------------------------+
  124. +---------+-------------------+---------------------------------+
  125. |  BYTE   |     CONTENTS      |          MEANING                |
  126. +---------+-------------------+---------------------------------+
  127. |  0-10   |  11 bytes         | field name in ASCII zero-filled |
  128. +---------+-------------------+---------------------------------+
  129. |  11     |  1 byte           | field type in ASCII             |
  130. |         |                   |  (C N L D or M)                 |
  131. +---------+-------------------+---------------------------------+
  132. |  12-15  |  32 bit number    | field data address              |
  133. |         |                   |  (address is set in memory)     |
  134. +---------+-------------------+---------------------------------+
  135. |  16     |  1 byte           | field length in binary          |
  136. +---------+-------------------+---------------------------------+
  137. |  17     |  1 byte           | field decimal count in binary   |
  138. +---------+-------------------+--------------------------------
  139. |  18-31  |  14 bytes         | reserved bytes (version 1.00)   |
  140. +---------+-------------------+---------------------------------+
  141. The data records are layed out as follows:
  142. 1. Data records are preceeded by one byte that is a
  143. space (20H) if the record is not deleted and an
  144. asterisk (2AH) if it is deleted.
  145. 2. Data fields are packed into records with no field
  146. separators or record terminators.
  147. 3. Data types are stored in ASCII format as follows:
  148. DATA TYPE      DATA RECORD STORAGE
  149. ---------      --------------------------------------------
  150. Character      (ASCII characters)
  151. Numeric        - . 0 1 2 3 4 5 6 7 8 9
  152. Logical        ? Y y N n T t F f  (? when not initialized)
  153. Memo           (10 digits representing a .DBT block number)
  154. Date           (8 digits in YYYYMMDD format, such as
  155. 19840704 for July 4, 1984)
  156.  
  157. This information came directly from the Ashton-Tate Forum.
  158. It can also be found in the Advanced Programmer's Guide available
  159. from Ashton-Tate.
  160.  
  161. One slight difference occurs between files created by dBASE III and those
  162. created by dBASE III Plus.  In the earlier files, there is an ASCII NUL
  163. character between the $0D end of header indicator and the start of the data.
  164. This NUL is no longer present in Plus, making a Plus header one byte smaller
  165. than an identically structured III file.
  166. *)
  167.  
  168. CONST
  169.   DB2File = 2;
  170.   DB3File = 3;
  171.   DB3WithMemo = $83;
  172.   ValidTypes : SET OF Char = ['C', 'N', 'L', 'M', 'D'];
  173.   MAX_HEADER = 4129;          { = maximum length of dBASE III header }
  174.   MAX_BYTES_IN_RECORD = 4000; { dBASE III record limit }
  175.   MAX_FIELDS_IN_RECORD = 128; { dBASE III field limit  }
  176.   BYTES_IN_MEMO_RECORD = 512; { dBASE III memo field record size }
  177.  
  178.   { Special Error codes for .DBF files }
  179.   NOT_DB_FILE = $80;  { first byte was not a $3 or $83 or a $2 (dBASE II)}
  180.   INVALID_FIELD = $81;{ invalid field type was found }
  181.   REC_TOO_HIGH = $82; { tried to read a record beyond the correct range }
  182.   PARTIAL_READ = $83; { only a partial record was read }
  183.  
  184.   (*
  185.   Although there are some declarations for memo files, the routines to access
  186.   them have not yet been implemented.
  187.   *)
  188.  
  189. TYPE
  190.   _HeaderType = ARRAY[0..MAX_HEADER] OF Byte;
  191.   _HeaderPrologType = ARRAY[0..31] OF Byte;
  192.   _FieldDescType = ARRAY[0..31] OF Byte;
  193.   _dRec = ^_DataRecord;
  194.   _DataRecord = ARRAY[0..MAX_BYTES_IN_RECORD] OF Byte;
  195.   _Str255 = STRING[255];
  196.   _Str80 = STRING[80];
  197.   _Str64 = STRING[64];
  198.   _Str10 = STRING[10];
  199.   _Str8 = STRING[8];
  200.   _Str2 = STRING[2];
  201.   _dbfFile = FILE;
  202.   _FieldRecord = RECORD
  203.                   Name : _Str10;
  204.                   Typ : Char;
  205.                   Len : Byte;
  206.                   Dec : Byte;
  207.                   Off : Integer;
  208.                 END;
  209.   _FieldArray = ARRAY[1..MAX_FIELDS_IN_RECORD] OF _FieldRecord;
  210.   _dFields = ^_FieldArray;
  211.   _MemoRecord = ARRAY[1..BYTES_IN_MEMO_RECORD] OF Byte;
  212.   _MemoFile = FILE OF _MemoRecord;
  213.   _StatusType = (NotOpen, NotUpdated, Updated);
  214.   dbfRecord = RECORD
  215.                 FileName : _Str64;
  216.                 dFile : _dbfFile;
  217.                 HeadProlog : _HeaderPrologType;
  218.                 dStatus : _StatusType;
  219.                 WithMemo : Boolean;
  220.                 DateOfUpdate : _Str8;
  221.                 NumRecs : Real;
  222.                 HeadLen : Integer;
  223.                 RecLen : Integer;
  224.                 NumFields : Integer;
  225.                 Fields : _dFields;
  226.                 CurRecord : _dRec;
  227.               END;
  228.  
  229.   VAR
  230.     dbfError : Integer; { global error indicators }
  231.     dbfOK  : Boolean;
  232.  
  233.   FUNCTION MakeReal(VAR b) : Real;
  234.     { takes a long 32-bit integer and converts it to a real }
  235.  
  236.   VAR
  237.     r : ARRAY[1..4] OF Byte ABSOLUTE b;
  238.  
  239.   BEGIN
  240.   MakeReal := (r[1]*1.0)+(r[2]*256.0)+(r[3]*65536.0)+(r[4]*16777216.0);
  241.   END;
  242.  
  243.   FUNCTION MakeUnsignedReal(VAR b) : Real;
  244.     { takes an unsigned 16-bit integer and converts it to a real }
  245.  
  246.   VAR
  247.     r : ARRAY[1..2] OF Byte ABSOLUTE b;
  248.  
  249.   BEGIN
  250.   MakeUnsignedReal := (r[1]*1.0)+(r[2]*256.0);
  251.   END;
  252.  
  253.   FUNCTION MakeInt(VAR b) : Integer;
  254.   VAR
  255.     i : Integer ABSOLUTE b;
  256.  
  257.   BEGIN
  258.   MakeInt := i;
  259.   END;
  260.  
  261.   FUNCTION MakeStr(b : Byte) : _Str2;
  262.   VAR
  263.     i : Integer;
  264.     s : _Str2;
  265.   BEGIN
  266.   i := b;
  267.   Str(i:2, s);
  268.   IF s[1] = ' ' THEN s[1] := '0';
  269.   MakeStr := s;
  270.   END;
  271.  
  272.   PROCEDURE GetDbfRecord(VAR D : dbfRecord; RecNum : Real);
  273.  
  274.   VAR
  275.     Result : Integer;
  276.  
  277.   BEGIN
  278.   IF RecNum > D.NumRecs THEN
  279.     BEGIN
  280.     dbfError := REC_TOO_HIGH;
  281.     dbfOK := FALSE;
  282.     Exit;
  283.     END;
  284.   {$I-} LongSeek(D.dFile, D.HeadLen+(RecNum-1)*D.RecLen); {$I+}
  285.   dbfError := IOResult;
  286.   IF dbfError = 0 THEN
  287.     BEGIN
  288.     {$I-} BlockRead(D.dFile, D.CurRecord^, D.RecLen, Result); {$I+}
  289.     dbfError := IOResult;
  290.     IF (dbfError = 0) AND (Result < D.RecLen) THEN
  291.       dbfError := PARTIAL_READ;
  292.     END;
  293.   dbfOK := (dbfError = 0);
  294.   END;                        {GetDbfRecord}
  295.  
  296.   PROCEDURE PutDbfRecord(VAR D : dbfRecord; RecNum : Real);
  297.  
  298.   VAR
  299.     Result : Integer;
  300.  
  301.   BEGIN
  302.   IF RecNum > D.NumRecs THEN
  303.     BEGIN
  304.     RecNum := D.NumRecs+1;
  305.     D.NumRecs := RecNum;
  306.     END;
  307.   {$I-} LongSeek(D.dFile, D.HeadLen+(RecNum-1)*D.RecLen); {$I+}
  308.   dbfError := IOResult;
  309.   IF dbfError = 0 THEN
  310.     BEGIN
  311.     {$I-} BlockWrite(D.dFile, D.CurRecord^, D.RecLen, Result); {$I+}
  312.     dbfError := IOResult;
  313.     END;
  314.   IF dbfError = 0 THEN D.dStatus := Updated;
  315.   dbfOK := (dbfError = 0);
  316.   END;                        {PutDbfRecord}
  317.  
  318.   PROCEDURE AppendDbf(VAR D : dbfRecord);
  319.  
  320.   BEGIN
  321.   PutDbfRecord(D, D.NumRecs+1);
  322.   END;
  323.  
  324.   PROCEDURE CloseDbf(VAR D : dbfRecord);
  325.  
  326.     PROCEDURE UpdateHeader(VAR D : dbfRecord);
  327.  
  328.     TYPE
  329.       RegType = RECORD CASE Byte OF
  330.         1 : (AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags : Integer);
  331.         2 : (AL, AH, BL, BH, CL, CH, DL, DH : Byte);
  332.                 END;
  333.  
  334.     VAR
  335.       Reg : RegType;
  336.       r : Real;
  337.  
  338.     BEGIN                     { UpdateHeader }
  339.     r := D.NumRecs;
  340.     Reg.AX := $2A00;          { Get DOS Date }
  341.     Intr($21, Reg);
  342.     IF D.HeadProlog[0] = DB2File THEN
  343.       BEGIN
  344.       D.HeadProlog[5] := Reg.CX-1900; {Year}
  345.       D.HeadProlog[3] := Reg.DH; {Month}
  346.       D.HeadProlog[4] := Reg.DL; {Day}
  347.       D.HeadProlog[2] := Trunc(r/256.0);
  348.       r := r-(D.HeadProlog[5]*256.0);
  349.       D.HeadProlog[1] := Trunc(r);
  350.       END
  351.     ELSE
  352.       BEGIN
  353.       D.HeadProlog[1] := Reg.CX-1900; {Year}
  354.       D.HeadProlog[2] := Reg.DH; {Month}
  355.       D.HeadProlog[3] := Reg.DL; {Day}
  356.       D.HeadProlog[7] := Trunc(r/16777216.0);
  357.       r := r-(D.HeadProlog[7]*16777216.0);
  358.       D.HeadProlog[6] := Trunc(r/65536.0);
  359.       r := r-(D.HeadProlog[6]*65536.0);
  360.       D.HeadProlog[5] := Trunc(r/256);
  361.       r := r-(D.HeadProlog[5]*256);
  362.       D.HeadProlog[4] := Trunc(r);
  363.       END;
  364.     {$I-}LongSeek(D.dFile, 0);{$I+}
  365.     dbfError := IOResult;
  366.     IF dbfError = 0 THEN
  367.       BEGIN
  368.       {$I-} BlockWrite(D.dFile, D.HeadProlog, 8); {$I+}
  369.       dbfError := IOResult;
  370.       END;
  371.     dbfOK := (dbfError = 0);
  372.     END;                      { UpdateHeader }
  373.  
  374.   CONST
  375.     EofMark : Byte = $1A;
  376.  
  377.   BEGIN                       { CloseDbf }
  378.   dbfError := 0;
  379.   IF D.dStatus = Updated THEN
  380.     BEGIN
  381.     UpdateHeader(D);
  382.     IF dbfError = 0 THEN
  383.       BEGIN
  384.       {$I-} LongSeek(D.dFile, D.HeadLen+D.NumRecs*D.RecLen); {$I+}
  385.       dbfError := IOResult;
  386.       END;
  387.     IF dbfError = 0 THEN
  388.       BEGIN
  389.       {$I-} BlockWrite(D.dFile, EofMark, 1); {$I+} {Put EOF marker }
  390.       dbfError := IOResult;
  391.       END;
  392.     END;   { IF Updated }
  393.   IF dbfError = 0 THEN
  394.     BEGIN
  395.     {$I-} Close(D.dFile);     {$I+}
  396.     dbfError := IOResult;
  397.     END;
  398.   IF dbfError = 0 THEN
  399.     BEGIN
  400.       D.dStatus := NotOpen;
  401.       FreeMem(D.CurRecord, D.RecLen);
  402.       FreeMem(D.Fields, D.NumFields*SizeOf(_FieldRecord));
  403.     END;
  404.   dbfOK := (dbfError = 0);
  405.   END;                        { CloseDbf }
  406.  
  407.   PROCEDURE OpenDbf(VAR D : dbfRecord);
  408.  
  409.     PROCEDURE ProcessHeader(VAR Header : _HeaderType; NumBytes : Integer);
  410.  
  411.       PROCEDURE GetOneFieldDesc(VAR F; VAR Field : _FieldRecord;
  412.                                 VAR Offset : Integer);
  413.  
  414.       VAR
  415.         i : Integer;
  416.         FD : _FieldDescType ABSOLUTE F;
  417.  
  418.       BEGIN                   { GetOneFieldDesc }
  419.       i := 0;
  420.       Field.Name := '';
  421.       REPEAT
  422.         Field.Name[Succ(i)] := Chr(FD[i]);
  423.         i := Succ(i);
  424.       UNTIL FD[i] = 0;
  425.       Field.Name[0] := Chr(i);
  426.       Field.Typ := Char(FD[11]);
  427.       IF D.HeadProlog[0] = DB2File THEN
  428.         BEGIN
  429.         Field.Len := FD[12];
  430.         Field.Dec := FD[15];
  431.         END
  432.       ELSE
  433.         BEGIN
  434.         Field.Len := FD[16];
  435.         Field.Dec := FD[17];
  436.         END;
  437.       Field.Off := Offset;
  438.       Offset := Offset+Field.Len;
  439.       IF NOT(Field.Typ IN ValidTypes) THEN
  440.         dbfError := INVALID_FIELD;
  441.       END;                    { GetOneFieldDesc }
  442.  
  443.       PROCEDURE ProcessDB2Header;
  444.  
  445.       VAR
  446.         o, i, tFieldsLen : Integer;
  447.         tempFields : _FieldArray;
  448.  
  449.       BEGIN   { ProcessDB2Header }
  450.       D.DateOfUpdate := MakeStr(Header[3])+'/'+MakeStr(Header[4])+'/'+MakeStr(Header[5]);
  451.       D.NumRecs := MakeUnsignedReal(Header[1]);
  452.       D.HeadLen := 521;
  453.       IF NumBytes < D.HeadLen THEN
  454.         BEGIN
  455.         dbfError := NOT_DB_FILE;
  456.         Close(D.dFile);
  457.         Exit;
  458.         END;
  459.       D.RecLen := MakeInt(Header[6]); { Includes the Deleted Record Flag }
  460.       GetMem(D.CurRecord, D.RecLen); { Allocate some memory for a buffer  }
  461.       D.dStatus := NotUpdated;
  462.       D.NumFields := 0;
  463.       Move(Header, D.HeadProlog, SizeOf(D.HeadProlog));
  464.       o := 1;                   {Offset within dbf record of current field }
  465.       i := 8;                   {Index for Header }
  466.       WHILE Header[i] <> $0D DO
  467.         BEGIN
  468.         D.NumFields := Succ(D.NumFields);
  469.         GetOneFieldDesc(Header[i], tempFields[D.NumFields], o);
  470.         IF dbfError <> 0 THEN
  471.           BEGIN
  472.           Close(D.dFile);
  473.           Exit;
  474.           END;
  475.         i := i+16;
  476.         END;                    { While Header[i] <> $0D }
  477.       tFieldsLen := D.NumFields*SizeOf(_FieldRecord);
  478.       GetMem(D.Fields, tFieldsLen);
  479.       Move(tempFields, D.Fields^, tFieldsLen);
  480.       D.WithMemo := FALSE;
  481.       END;                      {ProcessDB2Header}
  482.  
  483.     VAR
  484.       o, i : Integer;
  485.       tempFields : _FieldArray;
  486.  
  487.     BEGIN                     {ProcessHeader}
  488.     CASE Header[0] OF
  489.       DB2File : BEGIN
  490.                   ProcessDB2Header;
  491.                   Exit;
  492.                 END;
  493.       DB3File : D.WithMemo := False;
  494.       DB3WithMemo : D.WithMemo := True;
  495.       ELSE
  496.         BEGIN
  497.         dbfError := NOT_DB_FILE;
  498.         Close(D.dFile);
  499.         Exit;
  500.         END;
  501.       END;                      {CASE}
  502.     D.DateOfUpdate := MakeStr(Header[2])+'/'+MakeStr(Header[3])+'/'+MakeStr(Header[1]);
  503.     D.NumRecs := MakeReal(Header[4]);
  504.     D.HeadLen := MakeInt(Header[8]);
  505.     IF NumBytes < D.HeadLen THEN
  506.       BEGIN
  507.       dbfError := NOT_DB_FILE;
  508.       Close(D.dFile);
  509.       Exit;
  510.       END;
  511.     D.RecLen := MakeInt(Header[10]); { Includes the Deleted Record Flag }
  512.     GetMem(D.CurRecord, D.RecLen); { Allocate some memory for a buffer  }
  513.     D.dStatus := NotUpdated;
  514.     D.NumFields := 0;
  515.     Move(Header, D.HeadProlog, SizeOf(D.HeadProlog));
  516.     o := 1;                   {Offset within dbf record of current field }
  517.     i := 32;                  {Index for Header }
  518.     WHILE Header[i] <> $0D DO
  519.       BEGIN
  520.       D.NumFields := Succ(D.NumFields);
  521.       GetOneFieldDesc(Header[i], tempFields[D.NumFields], o);
  522.       IF dbfError <> 0 THEN
  523.         BEGIN
  524.         Close(D.dFile);
  525.         Exit;
  526.         END;
  527.       i := i+32;
  528.       END;                    { While Header[i] <> $0D }
  529.     i := D.NumFields*SizeOf(_FieldRecord);
  530.     GetMem(D.Fields,i) ;
  531.     Move(tempFields, D.Fields^, i);
  532.     END;                      {ProcessHeader}
  533.  
  534.     PROCEDURE GetHeader;
  535.  
  536.     VAR
  537.       Result : Integer;
  538.       H : _HeaderType;
  539.  
  540.     BEGIN                     { GetHeader }
  541.     {$I-} BlockRead(D.dFile, H, MAX_HEADER, Result); {$I+}
  542.     dbfError := IOResult;
  543.     IF dbfError = 0 THEN ProcessHeader(H, Result);
  544.     END;                      { GetHeader }
  545.  
  546.   BEGIN                       { OpenDbf }
  547.   Assign(D.dFile, D.FileName);
  548.   {$I-} Reset(D.dFile, 1); {$I+} {the '1' parameter sets the record size}
  549.   dbfError := IOResult;
  550.   IF dbfError = 0 THEN GetHeader;
  551.   dbfOK := (dbfError = 0);
  552.   END;                        { OpenDbf }
  553.  
  554.   PROCEDURE CreateDbf(VAR D : dbfRecord; fn : _Str64; n : Integer;
  555.                       flds : _dFields);
  556.     {
  557.     Call this procedure with the full pathname of the file that you want
  558.     to create (fn), the number of fields in a record (n), and a pointer
  559.     to an array of _FieldRecord (flds).  The procedure will initialize all
  560.     the data structures in the dbfRecord (D).
  561.     }
  562.  
  563.   VAR
  564.     tHeader : _HeaderType;
  565.  
  566.     PROCEDURE MakeFieldDescs;
  567.  
  568.       PROCEDURE MakeOneFieldDesc(VAR F; VAR Field : _FieldRecord);
  569.  
  570.       VAR
  571.         FD : _FieldDescType ABSOLUTE F;
  572.  
  573.       BEGIN                   { MakeOneFieldDesc }
  574.       Move(Field.Name[1],FD,Ord(Field.Name[0]));
  575.       FD[11] := Ord(Field.Typ);
  576.       FD[16] := Field.Len;
  577.       IF Field.Typ <> 'N' THEN Field.Dec := 0;
  578.       FD[17] := Field.Dec;
  579.       Field.Off := D.RecLen;
  580.       D.RecLen := D.RecLen+Field.Len;
  581.       IF NOT(Field.Typ IN ValidTypes) THEN dbfError := INVALID_FIELD;
  582.       IF Field.Typ = 'M' THEN D.WithMemo := TRUE;
  583.       END;                    { MakeOneFieldDesc }
  584.  
  585.     VAR
  586.       i : Integer;
  587.  
  588.     BEGIN                     {MakeFieldDescs}
  589.     D.RecLen := 1;
  590.     FOR i := 1 TO D.NumFields DO
  591.       BEGIN
  592.       MakeOneFieldDesc(tHeader[i*32],flds^[i]);
  593.       IF dbfError <> 0 THEN Exit;
  594.       END;
  595.     END;                      {MakeFieldDescs}
  596.  
  597.     PROCEDURE MakeHeader;
  598.  
  599.     VAR
  600.       Result : Integer;
  601.  
  602.     BEGIN                     { MakeHeader }
  603.     FillChar(tHeader,SizeOf(tHeader),#0);
  604.     D.WithMemo := FALSE;
  605.     D.HeadLen := Succ(D.NumFields) * 32;
  606.     tHeader[D.HeadLen] := $0D;
  607.     D.HeadLen := Succ(D.HeadLen);
  608.     tHeader[8] := Lo(D.HeadLen);
  609.     tHeader[9] := Hi(D.HeadLen);
  610.     MakeFieldDescs;
  611.     IF D.WithMemo THEN
  612.       tHeader[0] := DB3WithMemo
  613.     ELSE
  614.       tHeader[0] := DB3File;
  615.     tHeader[10] := Lo(D.RecLen);
  616.     tHeader[11] := Hi(D.RecLen);
  617.     END;                      { MakeHeader }
  618.  
  619.   VAR
  620.     i : Integer;
  621.  
  622.   BEGIN            { CreateDbf }
  623.   D.NumFields := n;
  624.   MakeHeader;
  625.   D.FileName := fn;
  626.   Assign(D.dFile, D.FileName);
  627.   {$I-} Rewrite(D.dFile, 1); {$I+} {Will overwrite if file exists!}
  628.   dbfError := IOResult;
  629.   IF dbfError = 0 THEN
  630.     BEGIN
  631.     {$I-} BlockWrite(D.dFile,tHeader,Succ(D.HeadLen));{$I+}
  632.     dbfError := IOResult;
  633.     END;
  634.   IF dbfError = 0 THEN
  635.     BEGIN
  636.     D.dStatus := Updated;
  637.     D.NumRecs := 0.0;
  638.     Move(tHeader,D.HeadProlog,SizeOf(D.HeadProlog));
  639.     D.DateOfUpdate := '  /  /  ';
  640.     GetMem(D.CurRecord, D.RecLen); { Allocate some memory for a buffer  }
  641.     FillChar(D.CurRecord^,D.RecLen,' ');
  642.     i := D.NumFields*SizeOf(_FieldRecord);
  643.     GetMem(D.Fields,i);
  644.     Move(flds, D.Fields^,i);
  645.     END;
  646.   dbfOK := (dbfError = 0);
  647.   END;                        { CreateDbf }
  648.  
  649. (* To enable the Demo program, delete the next line. *)
  650. (*
  651.  
  652.   PROCEDURE ErrorHalt(errorCode : Integer);
  653.     { a VERY crude error handler }
  654.   VAR
  655.     errorMsg : _Str80;
  656.  
  657.   BEGIN
  658.   CASE errorCode OF
  659.      00 : Exit;                { no error occurred }
  660.     $01 : errorMsg := 'Not found';
  661.     $02 : errorMsg := 'Not open for input';
  662.     $03 : errorMsg := 'Not open for output';
  663.     $04 : errorMsg := 'Just not open';
  664.     $91 : errorMsg := 'Seek beyond EOF';
  665.     $99 : errorMsg := 'Unexpected EOF';
  666.     $F0 : errorMsg := 'Disk write error';
  667.     $F1 : errorMsg := 'Directory full';
  668.     $F3 : errorMsg := 'Too many files';
  669.     $FF : errorMsg := 'Where did that file go?';
  670.     NOT_DB_FILE : errorMsg := 'Not a dBASE data file';
  671.     INVALID_FIELD : errorMsg := 'Invalid field type encountered';
  672.     REC_TOO_HIGH  : errorMsg := 'Requested record beyond range';
  673.     PARTIAL_READ  : errorMsg := 'Tried to read beyon EOF';
  674.     ELSE
  675.      errorMsg := 'Undefined error';
  676.     END;
  677.   WriteLn;
  678.   WriteLn(errorCode:3, ': ',errorMsg);
  679.   Halt(1);
  680.   END;
  681.  
  682. TYPE
  683.   PseudoStr = ARRAY[1..255] OF Char;
  684.  
  685. VAR
  686.   Demo : dbfRecord;
  687.   j, i : Integer;
  688.   blanks : _Str255;
  689.   SizeOfFile, r : Real;
  690.   fn : _Str64;
  691.  
  692.   PROCEDURE Wait;
  693.   VAR
  694.     c : Char;
  695.  
  696.   BEGIN
  697.   Write('Press any key to continue . . .');
  698.   Read(Kbd, c);
  699.   END;
  700.  
  701.  
  702.   PROCEDURE List(VAR D : dbfRecord);
  703.  
  704.     PROCEDURE ShowField(VAR a; VAR F : _FieldRecord);
  705.  
  706.     VAR
  707.       Data : PseudoStr ABSOLUTE a;
  708.  
  709.     BEGIN
  710.     WITH F DO
  711.       BEGIN
  712.       CASE Typ OF
  713.         'C', 'N', 'L' : Write(Copy(Data, 1, Len));
  714.         'M' : Write('Memo      ');
  715.         'D' : Write(Copy(Data, 5, 2), '/',
  716.               Copy(Data, 7, 2), '/',
  717.               Copy(Data, 1, 2));
  718.       END;                    {CASE}
  719.       IF Len <= Length(Name) THEN
  720.         Write(Copy(blanks, 1, Length(Name)-Pred(Len)))
  721.       ELSE
  722.         Write(' ');
  723.       END;                    {WITH F}
  724.     END;                      {ShowField}
  725.  
  726.   BEGIN                       {List}
  727.   WriteLn;
  728.   Write('Rec Num  ');
  729.   WITH D DO
  730.     BEGIN
  731.     FOR i := 1 TO NumFields DO
  732.       WITH Fields^[i] DO
  733.         IF Len >= Length(Name) THEN
  734.           Write(Name, Copy(blanks, 1, Succ(Len-Length(Name))))
  735.         ELSE
  736.           Write(Name, ' ');
  737.     WriteLn;
  738.     r := 1;
  739.     WHILE r <= NumRecs DO
  740.       BEGIN
  741.       GetDbfRecord(Demo, r);
  742.       IF NOT dbfOK THEN ErrorHalt(dbfError);
  743.       WriteLn;
  744.       Write(r:7:0, ' ');
  745.       Write(Chr(CurRecord^[0])); { the 'deleted' indicator }
  746.       FOR i := 1 TO NumFields DO
  747.         ShowField(CurRecord^[Fields^[i].Off], Fields^[i]);
  748.       r := r+1;
  749.       END;                    {WHILE r }
  750.     END;                      {WITH D }
  751.   END;                        {List}
  752.  
  753.   PROCEDURE DisplayStructure(VAR D : dbfRecord);
  754.  
  755.   VAR
  756.     i : Integer;
  757.  
  758.   BEGIN
  759.   WITH D DO
  760.     BEGIN
  761.     ClrScr;
  762.     Write(' #  Field Name   Type  Length  Decimal');
  763.     FOR i := 1 TO NumFields DO
  764.       BEGIN
  765.       WITH Fields^[i] DO
  766.         BEGIN
  767.         IF i MOD 20 = 0 THEN
  768.           BEGIN
  769.           WriteLn;
  770.           Wait;
  771.           ClrScr;
  772.           Write(' #  Field Name   Type  Length  Decimal');
  773.           END;
  774.         GoToXY(1, Succ(WhereY));
  775.         Write(i:2, Name:12, Typ:5, Len:9);
  776.         IF Typ = 'N' THEN Write(Dec:5);
  777.         END;                  {WITH Fields^}
  778.       END;                    {FOR}
  779.     WriteLn;
  780.     Wait;
  781.     END;                      {WITH D}
  782.   END;                        { DisplayStructure }
  783.  
  784.   PROCEDURE CopyDbf(fnDB2,fnDB3 : _Str64);
  785.     {
  786.     Copies a .DBF file to another file.  The SOURCE file may be a
  787.     II, III, or III Plus file.  The DESTINATION file will be a III Plus
  788.     file (although III will be able to use it with no problems).
  789.     }
  790.  
  791.   VAR
  792.     dOrg,dDest : dbfRecord;
  793.     recCount : Real;
  794.     x,y : Integer;
  795.     dummyPtr : _dRec;
  796.  
  797.   BEGIN             { CopyDbf }
  798.   dOrg.FileName := fnDB2;
  799.   OpenDbf(dOrg);
  800.   IF NOT dbfOK THEN ErrorHalt(dbfError);
  801.   CreateDbf(dDest,fnDB3,dOrg.NumFields,dOrg.Fields);
  802.   IF NOT dbfOK THEN ErrorHalt(dbfError);
  803.   dummyPtr := dDest.CurRecord;
  804.   dDest.CurRecord := dOrg.CurRecord;     { a dirty trick! }
  805.   recCount := 0;
  806.   WriteLn;
  807.   x := WhereX; y := WhereY;
  808.   Write(recCount:8:0,' Records Converted.');
  809.   WHILE recCount < dOrg.NumRecs DO
  810.     BEGIN
  811.     recCount := recCount + 1;
  812.     GetDbfRecord(dOrg,recCount);
  813.     IF NOT dbfOK THEN ErrorHalt(dbfError);
  814.     AppendDbf(dDest);     { go right into the append because both CurRecords }
  815.                           {  point to the same place }
  816.     IF NOT dbfOK THEN ErrorHalt(dbfError);
  817.     GotoXY(x,y);
  818.     Write(recCount:8:0);
  819.     END;
  820.   WriteLn;
  821.   CloseDbf(dOrg);
  822.   IF NOT dbfOK THEN ErrorHalt(dbfError);
  823.   dDest.CurRecord := dummyPtr;      { It is important to undo the dirty work! }
  824.   CloseDbf(dDest);
  825.   IF NOT dbfOK THEN ErrorHalt(dbfError);
  826.   END;              { CopyDbf }
  827.  
  828. VAR
  829.   fn1,fn2,p : _Str64;
  830.  
  831. BEGIN                         {Demonstration of DBF routines}
  832. WITH Demo DO
  833.   BEGIN
  834.   FillChar(blanks, SizeOf(blanks), $20);
  835.   blanks[0] := Chr(255);
  836.   ClrScr;
  837.   GoToXY(10, 10);
  838.   Write('Name of dBASE file (.DBF assumed): ');
  839.   Read(FileName);
  840.   IF Pos('.', FileName) = 0 THEN FileName := FileName+'.DBF';
  841.   OpenDbf(Demo);
  842.   IF NOT dbfOK THEN ErrorHalt(dbfError);
  843.   ClrScr;
  844.   SizeOfFile := LongFileSize(dFile);
  845.   WriteLn('File Name: ', FileName);
  846.   WriteLn('Date Of Last Update: ', DateOfUpdate);
  847.   WriteLn('Number of Records: ', NumRecs:10:0);
  848.   WriteLn('Size of File: ', SizeOfFile:15:0);
  849.   WriteLn('Length of Header: ', HeadLen:11);
  850.   WriteLn('Length of One Record: ', RecLen:7);
  851.   IF WithMemo THEN WriteLn('This file contains Memo fields.');
  852.   IF HeadProlog[0] = DB2File THEN WriteLn('dBASE 2.4 file');
  853.   Wait;
  854.   ClrScr;
  855.   DisplayStructure(Demo);
  856.   ClrScr;
  857.   List(Demo);
  858.   WriteLn;
  859.   Wait;
  860.   CloseDbf(Demo);
  861.   IF NOT dbfOK THEN ErrorHalt(dbfError);
  862.   END;                        {WITH}
  863. ClrScr;
  864. WriteLn('Enter the name of a dBASE file (II, III, or III +) to copy.');
  865. Write('Enter a blank name to exit: ');
  866. ReadLn(fn1);
  867. IF fn1 = '' THEN Halt;
  868. IF Pos('.', fn1) = 0 THEN fn1 := fn1+'.DBF';
  869. Write('Enter destination file name: ');
  870. ReadLn(fn2);
  871. IF Pos('.', fn2) = 0 THEN fn2 := fn2+'.DBF';
  872. CopyDbf(fn1,fn2);
  873. END.                          {of Demo program }
  874. *)
  875.  
  876.